﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
#if !SILVERLIGHT
using System.Threading;
#endif

namespace Gko.Utils
{
	/// <summary>
	/// Extensions to <see cref="T:System.EventHandler"/> and <see cref="T:System.EventHandler`1"/> delegates
	/// </summary>
	public static class EventHandlerExtensions
	{
		private class WaitList : IDisposable
		{
			private readonly List<IAsyncResult> waits = new List<IAsyncResult>();

			public void Add(IAsyncResult ar)
			{
				waits.Add(ar);
			}

			public void Dispose()
			{
				foreach (var ar in waits) {
					try {
						ar.AsyncWaitHandle.WaitOne();
#if !SILVERLIGHT
					} catch (AbandonedMutexException) {
#endif
					} catch (InvalidOperationException) {
					}
				}
				GC.SuppressFinalize(this);
			}
		}

		private static IEnumerable<T> GetDelegates<T>(this Delegate handler)
		{
			return handler.GetInvocationList().Cast<T>();
		}


		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1030:UseEventsWhereAppropriate")]
		public static bool Raise(this PropertyChangedEventHandler handler, object sender, string propertyName)
		{
			if (!handler.IsNull()) {
				handler(sender, new PropertyChangedEventArgs(propertyName));
				return true;
			}
			return false;
		}

#if !SILVERLIGHT
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1030:UseEventsWhereAppropriate")]
		public static bool Raise(this PropertyChangingEventHandler handler, object sender, string propertyName)
		{
			if (!handler.IsNull()) {
				handler(sender, new PropertyChangingEventArgs(propertyName));
				return true;
			}
			return false;
		}
#endif

		/// <summary>
		/// Raises the specified handler.
		/// </summary>
		/// <param name="handler">The handler.</param>
		/// <param name="sender">The sender.</param>
		/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
		/// <returns><c>true</c> if handler is rised; otherwise, <c>false</c>.</returns>
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1030:UseEventsWhereAppropriate")]
		public static bool Raise(this EventHandler handler, object sender, EventArgs e)
		{
			if (!handler.IsNull()) {
				handler(sender, e);
				return true;
			}
			return false;
		}

		/// <summary>
		/// Raises the specified handler.
		/// </summary>
		/// <param name="handler">The handler.</param>
		/// <param name="sender">The sender.</param>
		/// <returns><c>true</c> if handler is rised; otherwise, <c>false</c>.</returns>
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1030:UseEventsWhereAppropriate")]
		public static bool Raise(this EventHandler handler, object sender)
		{
			return handler.Raise(sender, EventArgs.Empty);
		}

		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1030:UseEventsWhereAppropriate")]
		public static bool Raise(this EventHandler handler, EventArgs e)
		{
			return Raise(handler, null, e);
		}

		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1030:UseEventsWhereAppropriate")]
		public static bool Raise(this EventHandler handler)
		{
			return Raise(handler, null);
		}


		/// <summary>
		/// Raises the specified handler.
		/// </summary>
		/// <typeparam name="TEventArgs">The type of the event args.</typeparam>
		/// <param name="handler">The handler.</param>
		/// <param name="sender">The sender.</param>
		/// <param name="e">The <see cref="T:System.EventArgs"/> instance containing the event data.</param>
		/// <returns><c>true</c> if handler is rised; otherwise, <c>false</c>.</returns>
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1030:UseEventsWhereAppropriate")]
		public static bool Raise<TEventArgs>(this EventHandler<TEventArgs> handler, object sender, TEventArgs e) where TEventArgs : EventArgs
		{
			if (handler.IsNull()) {
				return false;
			}
			handler(sender, e);
			return true;
		}

		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1030:UseEventsWhereAppropriate")]
		public static bool Raise<TEventArgs>(this EventHandler<TEventArgs> handler, TEventArgs e) where TEventArgs : EventArgs
		{
			return Raise(handler, null, e);
		}

		/// <summary>
		/// Raises the specified handler asynchronously.
		/// </summary>
		/// <param name="handler">The handler.</param>
		/// <param name="sender">The sender.</param>
		/// <returns><c>true</c> if handler is rised; otherwise, <c>false</c>.</returns>
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1030:UseEventsWhereAppropriate")]
		public static bool RaiseAsync(this EventHandler handler, object sender)
		{
			return handler.RaiseAsync(sender, EventArgs.Empty);
		}

		/// <summary>
		/// Raises the specified handler asynchronously.
		/// </summary>
		/// <typeparam name="TEventArgs">The type of the event args.</typeparam>
		/// <param name="handler">The handler.</param>
		/// <param name="sender">The sender.</param>
		/// <param name="e">The <see cref="T:System.EventArgs"/> instance containing the event data.</param>
		/// <returns><c>true</c> if handler is rised; otherwise, <c>false</c>.</returns>
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1030:UseEventsWhereAppropriate")]
		public static bool RaiseAsync<TEventArgs>(this EventHandler<TEventArgs> handler, object sender, TEventArgs e) where TEventArgs : EventArgs
		{
			if (handler.IsNull()) {
				return false;
			}
			// TODO: warning CS1911: Access to member 'System.EventHandler<TEventArgs>.EndInvoke(System.IAsyncResult)' through a 'base' keyword from an anonymous method, lambda expression, query expression, or iterator results in unverifiable code. Consider moving the access into a helper method on the containing type.
			handler.GetDelegates<EventHandler<TEventArgs>>().ForEach(d => d.BeginInvoke(sender, e, d.EndInvoke, null));
			return true;
		}

		/// <summary>
		/// Raises the specified handler asynchronously.
		/// </summary>
		/// <param name="handler">The handler.</param>
		/// <param name="sender">The sender.</param>
		/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
		/// <returns><c>true</c> if handler is rised; otherwise, <c>false</c>.</returns>
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1030:UseEventsWhereAppropriate")]
		public static bool RaiseAsync(this EventHandler handler, object sender, EventArgs e)
		{
			if (handler.IsNull()) {
				return false;
			}
			// TODO: warning CS1911: Access to member 'System.EventHandler.EndInvoke(System.IAsyncResult)' through a 'base' keyword from an anonymous method, lambda expression, query expression, or iterator results in unverifiable code. Consider moving the access into a helper method on the containing type.
			handler.GetDelegates<EventHandler>().ForEach(d => d.BeginInvoke(sender, e, d.EndInvoke, null));
			return true;
		}

		/// <summary>
		/// Converts the specified handler.
		/// </summary>
		/// <typeparam name="TEventArgs">The type of the event args.</typeparam>
		/// <param name="handler">The handler.</param>
		/// <returns>Nongeneric <see cref="T:System.EventHandler"/></returns>
		public static EventHandler Convert<TEventArgs>(this EventHandler<TEventArgs> handler) where TEventArgs : EventArgs
		{
			return (sender, e) => handler.Raise(sender, (TEventArgs)e);
		}

		/// <summary>
		/// Converts the specified handler.
		/// </summary>
		/// <typeparam name="TEventArgs">The type of the event args.</typeparam>
		/// <param name="handler">The handler.</param>
		/// <returns>Generic <see cref="T:System.EventHandler`1"/></returns>
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter")]
		public static EventHandler<TEventArgs> Convert<TEventArgs>(this EventHandler handler) where TEventArgs : EventArgs
		{
			return (sender, e) => handler.Raise(sender, e);
		}

		/// <summary>
		/// Raises the specified handler and waits.
		/// </summary>
		/// <typeparam name="TEventArgs">The type of the event args.</typeparam>
		/// <param name="handler">The handler.</param>
		/// <param name="sender">The sender.</param>
		/// <param name="e">The <see cref="T:System.EventArgs"/> instance containing the event data.</param>
		/// <returns><c>true</c> if handler is rised; otherwise, <c>false</c>.</returns>
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1030:UseEventsWhereAppropriate")]
		public static bool RaiseParallel<TEventArgs>(this EventHandler<TEventArgs> handler, object sender, TEventArgs e) where TEventArgs : EventArgs
		{
			if (handler.IsNull()) {
				return false;
			}
			using (var w = new WaitList()) {
				handler.GetDelegates<EventHandler<TEventArgs>>().Select(d => d.BeginInvoke(sender, e, null, null)).ForEach(w.Add);
			}
			return true;
		}

		/// <summary>
		/// Raises the specified handler and waits.
		/// </summary>
		/// <param name="handler">The handler.</param>
		/// <param name="sender">The sender.</param>
		/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
		/// <returns><c>true</c> if handler is rised; otherwise, <c>false</c>.</returns>
		[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1030:UseEventsWhereAppropriate")]
		public static bool RaiseParallel(this EventHandler handler, object sender, EventArgs e)
		{
			if (handler.IsNull()) {
				return false;
			}
			using (var w = new WaitList()) {
				handler.GetDelegates<EventHandler>().Select(d => d.BeginInvoke(sender, e, null, null)).ForEach(w.Add);
			}
			return true;
		}

	}
}